
#include "AObject.h"

/*
class MemPlex
{
protected:
	MemPlex* pNext;
public:
	void* data() { return this+1; }
	static MemPlex* Create(MemPlex*& head, UINT nMax, UINT cbElement);
	void FreeDataChain();      // free this one and links
};

inline MemPlex* MemPlex::Create(MemPlex*& pHead, UINT nMax, UINT cbElement)
{
	MemPlex* p = (MemPlex*)gMemAlloc(sizeof(MemPlex) + nMax * cbElement);
	p->pNext = pHead;
	pHead = p;  // change head (adds in reverse order for simplicity)
	return p;
}

inline void MemPlex::FreeDataChain()       // free this one and links
{
	MemPlex* p = this;
	while (p)
	{
		BYTE* bytes = (BYTE*) p;
		MemPlex* pNext = p->pNext;
		gMemFree(bytes);
		p = pNext;
	}
}
*/


#if !defined(_USE_MEM_GUARDER_)
#define OSMemAlloc malloc
#define OSMemFree free
	void* gMemAlloc(unsigned int size)
	{
		void* pv = OSMemAlloc(size);
		//gmemzero(pv, size);
		return pv;
	}

	void *gMemAllocAligned (int nAlign, unsigned int nSize, void** ppvRealAddr)
	{
		*ppvRealAddr = gMemAlloc(nSize + nAlign);
		int r = (int)(*ppvRealAddr);
		r = (r + nAlign - 1) / nAlign * nAlign;
		return (void*)r;
	}

	void gMemFree(void* pv)
	{
		OSMemFree(pv);
	}
#else //!_USE_MEM_GUARDER_
	//debug tracing system, single thread safe
#define gstrlen strlen
#define gitoa _itoa_s
#define gmemcpy memcpy
#define gmemset memset
#define gmemmove memmove

	int FunctionGuard::m_nDepth = 0;
	char FunctionGuard::m_szCallStack[MAX_FUNCTION_DEPTH][MAX_LEN_OF_FUNCTION_NAME];
	
	static const char* getShortFileName(const char* pszFileName)
	{
		const char* psz = pszFileName + gstrlen(pszFileName) - 1;
		while (*psz != '/' && *psz != '\\' && psz > pszFileName) 
		{ 
			psz--; 
		}
		return psz;
	}
	
	int FunctionGuard::FunctionEnter (const char *szFunc, const char* pszfile, int line)
	{
		if (m_nDepth < MAX_FUNCTION_DEPTH - 1)
		{
			int lenfunc, lenfile, lenline;
			lenfunc = lenfile = lenline = 0;

			lenfunc = gstrlen(szFunc);
	
			const char* psz = getShortFileName(pszfile);
			pszfile = psz;
			lenfile = gstrlen(pszfile);

			char szline[16];
			szline[0] = ':';
			gitoa(line, szline + 1, 14, 10);
			lenline = gstrlen(szline);
			
			//lenline, lenfunc, lenfile, pszfile, szline are refinied

			int pos = 0;
			int wantcopylen;
			int cancopylen;
			
			wantcopylen = lenfunc;
			cancopylen = MAX_LEN_OF_FUNCTION_NAME - pos - 1;
			gmemcpy(m_szCallStack[m_nDepth] + pos, szFunc, min(cancopylen, wantcopylen));
			pos += lenfunc;
			_ASSERT(pos < MAX_LEN_OF_FUNCTION_NAME - 1);
			
			wantcopylen = lenfile;
			cancopylen = MAX_LEN_OF_FUNCTION_NAME - pos - 1;
			gmemcpy(m_szCallStack[m_nDepth] + pos, pszfile, min(cancopylen, wantcopylen));
			pos += lenfile;
			_ASSERT(pos < MAX_LEN_OF_FUNCTION_NAME - 1);
			
			wantcopylen = lenline;
			cancopylen = MAX_LEN_OF_FUNCTION_NAME - pos - 1;
			gmemcpy(m_szCallStack[m_nDepth] + pos, szline, min(cancopylen, wantcopylen));
			pos += lenline;
			_ASSERT(pos < MAX_LEN_OF_FUNCTION_NAME - 1);
			
			m_szCallStack[m_nDepth][pos] = 0;
			
			m_nDepth++;
		}
		else if (MAX_FUNCTION_DEPTH - 1 == m_nDepth)
		{
			const char *psz = "*stack overflow*";
			int len = 0;
			const char *pszc = psz;
			while (*pszc++)
				len++;
			gmemcpy (m_szCallStack[m_nDepth], psz, len + 1);
			m_nDepth++;
		}
		else
		{
			//fuck, the stack is overflow allready.
			m_nDepth++;	//for the destructor runs correctly
		}
		return m_nDepth;
	}

	int FunctionGuard::FunctionLeave ()
	{
		_ASSERT (m_nDepth > 0);
		m_nDepth--;
		return m_nDepth;
	}

	int FunctionGuard::GetCallStackStringLen (int dep)
	{
		if (-1 == dep)
			dep = m_nDepth;
		if (dep < 0)
			return 0;
		int slen = 0;
		for (int i = min (dep, MAX_FUNCTION_DEPTH) - 1; i >= 0; i--)
		{
			const char *psz = m_szCallStack[i];
			while (*psz++)
				slen++;
			slen++;	//for '/'
		}
		return slen;
	}

	int FunctionGuard::GetCallStack (char *szcs, int len, int dep)
	{
		if (-1 == dep)
			dep = m_nDepth;
		if (dep < 0)
			return 0;

		int slen = 0;
		len--;
		//m_nDepth can be bigger than MAX_FUNCTION_DEPTH
		for (int i = min (dep, MAX_FUNCTION_DEPTH) - 1; slen < len && i >= 0; i--)
		{
			const char *psz = m_szCallStack[i];
			while (*psz && slen < len)
			{
				*(szcs + slen++) = *psz++;
			}
			if (i && slen < len)
				*(szcs + slen++) = '/';
		}
		szcs[slen] = 0;
		return slen;
	}

	typedef struct tagMemBlockInfo
	{
		unsigned int nMagic;
		unsigned int nSize;
		unsigned int nAllocSize;
		unsigned int nRealAddr;
	}
	TMemBlockInfo;

	// ---------------------------------------------------------------------------------------
	// MemHeader ....       MemBlockInfo    |AlignPointpvRet        | end_fill      | callstack
	// ---------------------------------------------------------------------------------------
	// | nMemPtrOffset      |                               |nSize align in 4       | 16 bytes      | callstack CAString
	// ---------------------------------------------------------------------------------------
	// | bytes <= align(nAlign + info)      |nSize align in 4       | 16 bytes      | callstack CAString

	#define MAGIC_OF_MEM_BLOCK	0xca2e3e3b
	#define BYTE_FILL			0xca
	#define SIZE_OF_END_FILL	16

	static BOOL _IsMemDamaged (unsigned char *pb, int size)
	{
		for (int i = 0; i < size; i++)
		{
			if (*(pb + i) != BYTE_FILL)
			{
				return true;
			}
		}
		return false;
	}

	//do not use the List
	//List AddNewNode will call new again
	//these will recure and cause an error.
	//#include "List.h"
	class _ULEMemProtector
	{
		unsigned int m_nSizeAlloced;
		unsigned int m_nMaxSizeAlloced;

		int m_nSlotCount;
		int m_nSlotIndex;
		unsigned int *m_pdwSlots;

		int _GetSlot ()
		{
			int nIndexNew;
			if (m_nSlotIndex >= m_nSlotCount)
			{
				//realloc some slots
				int nNewSlotCount = (m_nSlotCount + 16);

				unsigned int *pdwNew = (unsigned int *) malloc (sizeof (unsigned int) * nNewSlotCount);

				gmemcpy (pdwNew, m_pdwSlots, m_nSlotCount * sizeof (unsigned int));
				gmemset (pdwNew + m_nSlotCount, 0, sizeof (unsigned int) * 16);
				free (m_pdwSlots);
				
				m_nSlotCount = nNewSlotCount;
				m_pdwSlots = pdwNew;
			}
			nIndexNew = m_nSlotIndex;
			m_nSlotIndex++;

			return nIndexNew;
		}
	public:
		_ULEMemProtector ()
		{
			m_pdwSlots = NULL;
			m_nSlotCount = 0;
			m_nSlotIndex = 0;
			m_nSizeAlloced = 0;
			m_nMaxSizeAlloced = 0;
		};

		unsigned int getMaxSizeAlloced() const { return m_nMaxSizeAlloced; }
		unsigned int getSizeAlloced() const { return m_nSizeAlloced; }

		void AddMemBlock (TMemBlockInfo * pmem)
		{
			*(m_pdwSlots + _GetSlot ()) = (unsigned int) pmem;
			m_nSizeAlloced += pmem->nSize;
			if (m_nSizeAlloced > m_nMaxSizeAlloced)
				m_nMaxSizeAlloced = m_nSizeAlloced;
		}
		int DelMemBlock (TMemBlockInfo * pmem)
		{
			m_nSizeAlloced -= pmem->nSize;

			for (int i = 0; i < m_nSlotIndex; i++)
			{
				if (*(m_pdwSlots + i) == (unsigned int) pmem)
				{
					//find this
					*(m_pdwSlots + i) = 0;
					int nCount = m_nSlotIndex - i - 1;
					if (nCount > 0)
					{
						gmemmove (m_pdwSlots + i, m_pdwSlots + i + 1, nCount * sizeof (unsigned int));
					}
					m_nSlotIndex--;
					return i;
				}
			}
			return -1;
		}
		void DumpMemoryAllocInfo (BOOL bDetails)
		{
			_Warning ("\r\n");
			_Warning ("===================================================\r\n");
			_Warning ("Dump Memory Allocation Info \r\n");
			_Warning ("Bytes Allocated: %d / Max Bytes Allocated:%d\n",
				m_nSizeAlloced, m_nMaxSizeAlloced);

			//report the memory leaks
			if (m_nSlotIndex && bDetails)
			{
				_Warning ("===================================================\r\n");
				_Warning ("Dump Memory Block Info \r\n");
				TMemBlockInfo *pmem;
				for (int i = 0; i < m_nSlotIndex; i++)
				{
					pmem = (TMemBlockInfo *) * (m_pdwSlots + i);

					int len;
					char sz16CallStack[1024];
					const char *psz;

					len = 0;
					psz = (const char *) (pmem + 1) + pmem->nAllocSize +
						SIZE_OF_END_FILL;
					while (len < (int) (SIZE_OF_ARRAY (sz16CallStack) - 2)
						   && *psz)
					{
						if (*psz != '/')
						{
							sz16CallStack[len++] = *psz;
						}
						else
						{
							sz16CallStack[len++] = '\r';
							sz16CallStack[len++] = '\n';
						}
						psz++;
					}
					sz16CallStack[len++] = 0;

					_Warning ("%p bytes:%8d from:\n%s\n", pmem + 1, pmem->nSize,
						sz16CallStack);
				}
				_Warning ("===================================================\r\n");
				_Warning ("\r\n");
			}
			_Warning ("Bytes Allocated: %d / Max Bytes Allocated:%d\n",
				m_nSizeAlloced, m_nMaxSizeAlloced);
			_Warning ("Dump Memory Allocation Info End\r\n");
			_Warning ("===================================================\r\n");
		}
		void CheckMemoryBlocks ()
		{
			//report the memory leaks
			if (m_nSlotIndex)
			{
				for (int i = 0; i < m_nSlotIndex; i++)
				{
					TMemBlockInfo *pbi = (TMemBlockInfo *) * (m_pdwSlots + i);
					BOOL bDamaged = MAGIC_OF_MEM_BLOCK != pbi->nMagic;
					if (!bDamaged)
						bDamaged |=
						_IsMemDamaged (((unsigned char *) (pbi + 1)) + pbi->nSize,
						SIZE_OF_END_FILL + pbi->nAllocSize - pbi->nSize);
					_ASSERT (!bDamaged);
				}
			}
		}
		~_ULEMemProtector ()
		{
			//_Warning ("\n\n*** Dumping Memory Leak Info");
			gDumpMemoryAllocInfo(m_nSlotIndex != 0);
			//_Warning ("*** Dumping Memory Info Leak End\n");
			//while (m_nSlotIndex)
			{
				//dead loop here
			}
		}
	};

	static _ULEMemProtector __s_care_memprotector;
	void gDumpMemoryAllocInfo (BOOL bDetails)
	{
		__s_care_memprotector.DumpMemoryAllocInfo (bDetails);
	}
	 
	void gCheckMemoryBlocks()
	{
		__s_care_memprotector.CheckMemoryBlocks();
	}

	unsigned int gGetMemSizeAlloced() 
	{
		return __s_care_memprotector.getSizeAlloced();
	}
	unsigned int gGetMemMaxSizeAlloced()
	{
		return __s_care_memprotector.getMaxSizeAlloced();
	}
	
	void *gMemAlloc (unsigned int nSize)
	{
		return gMemAllocAligned (4, nSize, NULL);
	}
	
	void *gMemAllocAligned (int nAlign, unsigned int nSize, void** ppvRealAddr)
	{
		if (nAlign & 3)
		{
			_Warning ("nAlign must align in 4 bytes at least\n");
			while (1);
		}
		int hsize = ALIGN_IN_4BYTES (sizeof (TMemBlockInfo) + nAlign);
		int asize = ALIGN_IN_4BYTES (nSize);
		int cslen = ALIGN_IN_4BYTES (FunctionGuard::GetCallStackStringLen () + 4);
		int memsize = asize + hsize + cslen + SIZE_OF_END_FILL;
		
		//MBI           ->      memused         ->      endfill->       callstack
		//sizeof(MBI)   nSize(in 4)             16 bytes        ??(in 4)
		void *pv = malloc (memsize);
		if (NULL == pv)
		{
			gDumpMemoryAllocInfo (false);
			_ASSERT (false);
		}
		gmemset (pv, BYTE_FILL, memsize);

		int addr = (int) pv;
		addr += sizeof (TMemBlockInfo);
		addr += nAlign - 1;
		addr /= nAlign;
		addr *= nAlign;
		
		TMemBlockInfo *pbi = (TMemBlockInfo *) (addr - sizeof (TMemBlockInfo));
		
		pbi->nMagic = MAGIC_OF_MEM_BLOCK;
		pbi->nSize = nSize;
		pbi->nAllocSize = asize;
		pbi->nRealAddr = (int) pv;

		char *pszCallstack = (char *) (pbi + 1) + asize + SIZE_OF_END_FILL;
		FunctionGuard::GetCallStack (pszCallstack, cslen);
		
		gmemset (pbi + 1, 0xcc, nSize);
		
		__s_care_memprotector.AddMemBlock (pbi);
		
		void* pvret = (void*) (pbi + 1);
		if (ppvRealAddr)
		{
			//some program will use this pointer to call gMemFree
			*ppvRealAddr = pvret;
		}
		return pvret;
	}
	
	void gMemFree (void *pv)
	{
		if (NULL == pv)
			return;
		TMemBlockInfo *pbi;
		
		pbi = ((TMemBlockInfo *) pv) - 1;
		if (MAGIC_OF_MEM_BLOCK != pbi->nMagic)
		{
			//should be delete[] ?
			pbi = ((TMemBlockInfo *)((PBYTE)pv - 4)) - 1;
			if (MAGIC_OF_MEM_BLOCK == pbi->nMagic)
			{
				//should call "delete [] p" !!!!!!
				//not "delete p"
				_ASSERT(false);
			}
		}
		BOOL bDamaged = MAGIC_OF_MEM_BLOCK != pbi->nMagic;
		
		if (!bDamaged)
			bDamaged |=
			_IsMemDamaged (((unsigned char *) (pbi + 1)) + pbi->nSize,
						   SIZE_OF_END_FILL + pbi->nAllocSize - pbi->nSize);
		_ASSERT (!bDamaged);
		if (!bDamaged)
		{
			__s_care_memprotector.DelMemBlock (pbi);
			free ((void *) (pbi->nRealAddr));
		}
		else
		{
			//ai~~~
		}
	}
#endif //!_USE_MEM_GUARDER_

EAnimationMode String2AnimateMode(const CAString& mode)
{
	EAnimationMode m;

	if (mode == "abca") m = AM_LOOP;
	else if (mode == "abcb") m = AM_REVERSE;
	else if (mode == "abcc") m = AM_EXTENDS;
	else if (mode == "abc0") m = AM_CLAMP;
	else m = AM_LOOP;

	return m;
}
